/*
 * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores
 * CA 94065 USA or visit www.oracle.com if you need additional information or
 * have any questions.
 */
package com.codename1.ui.html;

import com.codename1.ui.Component;
import com.codename1.xml.Element;

import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;

/**
 * The HTMLElement class defines a single HTML element with its attributes and children.
 * Due to its hierarchial nature, this class can be used for a single "leaf" Element, for more complex elements (with child elements), and up to describing the entire document.
 *
 * @author Ofir Leitner
 */
public class HTMLElement extends Element {

//////////////////////////////////
// Tags                         //
    /// ///////////////////////////////

    //HTML Tag ID codes:
    public static final int TAG_CSS_ILLEGAL_SELECTOR = -3; // If this is the tag ID it means this selector was malformed and as such will never match (For example p[] or a[=] - empty/meaningless attribute selection). Note that we can't simply discard this selector for the case of descendants: for example 'h1 a[] h2' - should never be matched - if we would discard the a[] which is illegal we would get 'h1 h2' which may be matched
    public static final int TAG_CSS_SELECTOR = -2;
    public static final int TAG_UNSUPPORTED = -1;

    //Structure Module
    public static final int TAG_BODY = 0;
    public static final int TAG_HEAD = 1;
    public static final int TAG_HTML = 2;
    public static final int TAG_TITLE = 3;

    //Text Module -
    public static final int TAG_ABBR = 4;    // No visual effect
    public static final int TAG_ACRONYM = 5; // No visual effect
    public static final int TAG_ADDRESS = 6; // No visual effect
    public static final int TAG_BLOCKQUOTE = 7;
    public static final int TAG_BR = 8;
    public static final int TAG_CITE = 9;
    public static final int TAG_CODE = 10;
    public static final int TAG_DFN = 11;
    public static final int TAG_DIV = 12;
    public static final int TAG_EM = 13;
    public static final int TAG_H1 = 14;
    public static final int TAG_H2 = 15;
    public static final int TAG_H3 = 16;
    public static final int TAG_H4 = 17;
    public static final int TAG_H5 = 18;
    public static final int TAG_H6 = 19;
    public static final int TAG_KBD = 20;
    public static final int TAG_P = 21;
    public static final int TAG_PRE = 22;
    public static final int TAG_Q = 23;
    public static final int TAG_SAMP = 24;
    public static final int TAG_SPAN = 25;   // While this is not parsed, it will be kept in the DOM and as such CSS will affect its class/ID
    public static final int TAG_STRONG = 26;
    public static final int TAG_VAR = 27;

    //Hypertext Module -
    public static final int TAG_A = 28;

    //List Module -
    public static final int TAG_DL = 29;
    public static final int TAG_DT = 30;
    public static final int TAG_DD = 31;
    public static final int TAG_OL = 32;
    public static final int TAG_UL = 33;
    public static final int TAG_LI = 34;

//Basic Forms Module -

    public static final int TAG_FORM = 35;
    public static final int TAG_INPUT = 36;
    public static final int TAG_LABEL = 37;
    public static final int TAG_SELECT = 38;
    public static final int TAG_OPTION = 39;
    public static final int TAG_TEXTAREA = 40;

    //Basic Tables Module -
    public static final int TAG_CAPTION = 41;
    public static final int TAG_TABLE = 42;
    public static final int TAG_TD = 43;
    public static final int TAG_TH = 44;
    public static final int TAG_TR = 45;

    //Image Module -
    public static final int TAG_IMG = 46;

    //Object Module -
    public static final int TAG_OBJECT = 47; // Not supported
    public static final int TAG_PARAM = 48;  // Not supported

    //Metainformation Module
    public static final int TAG_META = 49;

    //Link Module -
    public static final int TAG_LINK = 50;

    //Base Module
    public static final int TAG_BASE = 51;

    //XHTML Mobile Profile additons
    public static final int TAG_HR = 52;
    public static final int TAG_OPTGROUP = 53; // Currently still not supported as Codename One's ComboBox can't display option groups, but will display as a regular ComboBox
    public static final int TAG_STYLE = 54;
    public static final int TAG_B = 55;
    public static final int TAG_I = 56;
    public static final int TAG_BIG = 57;
    public static final int TAG_SMALL = 58;
    public static final int TAG_FIELDSET = 59;

    // HTML 4 tags - the following tags are not part of the XHTML MP 1.0 standard:

    public static final int TAG_U = 60; // Underline
    public static final int TAG_FONT = 61;
    public static final int TAG_DEL = 62; // Rendered same as S
    public static final int TAG_INS = 63; // Rendered same as U
    public static final int TAG_TT = 64;
    public static final int TAG_BASEFONT = 65;
    public static final int TAG_MENU = 66; // Same as UL
    public static final int TAG_S = 67; // Strike through
    public static final int TAG_STRIKE = 68; // Strike through
    public static final int TAG_CENTER = 69; // shorthand for DIV align=center
    public static final int TAG_DIR = 70;// Same as UL
    public static final int TAG_MAP = 71;
    public static final int TAG_AREA = 72;
    public static final int TAG_LEGEND = 73;
    public static final int TAG_SUB = 74;
    public static final int TAG_SUP = 75;
    public static final int TAG_NOSCRIPT = 76; // Since we don't support scripts this tag will always be evaluated
    public static final int TAG_NOFRAMES = 77; // Since we don't support frames this tag will always be evaluated
    public static final int TAG_THEAD = 78;
    public static final int TAG_TBODY = 79;
    public static final int TAG_TFOOT = 80;

    public static final int TAG_BUTTON = 81;
    //Text nodes (not an actual tag - text segments are added by the parser as the 'text' tag
    public static final int TAG_TEXT = 82;
    /// ///////////////////////////////

//Tag attributes:
    public static final int ATTR_CLASS = 0;
    public static final int ATTR_ID = 1;


//////////////////////////////////
// Attributes                   //
    public static final int ATTR_STYLE = 2;
    public static final int ATTR_TITLE = 3;
    public static final int ATTR_XMLNS = 4;
    public static final int ATTR_XMLLANG = 5;
    public static final int ATTR_ALIGN = 6;
    public static final int ATTR_BGCOLOR = 7;
    public static final int ATTR_LINK = 8;
    public static final int ATTR_TEXT = 9;
    public static final int ATTR_VERSION = 10;
    public static final int ATTR_CITE = 11;
    public static final int ATTR_ACCESSKEY = 12;
    public static final int ATTR_CHARSET = 13;
    public static final int ATTR_HREF = 14;
    public static final int ATTR_HREFLANG = 15;
    public static final int ATTR_REL = 16;
    public static final int ATTR_REV = 17;
    public static final int ATTR_TABINDEX = 18;
    public static final int ATTR_TYPE = 19;
    public static final int ATTR_ACTION = 20;
    public static final int ATTR_ENCTYPE = 21;
    public static final int ATTR_METHOD = 22;
    public static final int ATTR_WIDTH = 23;
    public static final int ATTR_HEIGHT = 24;
    public static final int ATTR_ALT = 25;
    public static final int ATTR_HSPACE = 26;
    public static final int ATTR_VSPACE = 27;
    public static final int ATTR_LONGDESC = 28;
    public static final int ATTR_LOCALSRC = 29;
    public static final int ATTR_SRC = 30;
    public static final int ATTR_SIZE = 31;
    public static final int ATTR_CHECKED = 32;
    public static final int ATTR_EMPTYOK = 33;
    public static final int ATTR_FORMAT = 34;
    public static final int ATTR_ISTYLE = 35;
    public static final int ATTR_MAXLENGTH = 36;
    public static final int ATTR_NAME = 37;
    public static final int ATTR_VALUE = 38;
    public static final int ATTR_FOR = 39;
    public static final int ATTR_XMLSPACE = 40;
    public static final int ATTR_MULTIPLE = 41;
    public static final int ATTR_SELECTED = 42;
    public static final int ATTR_ABBR = 43;
    public static final int ATTR_AXIS = 44;
    public static final int ATTR_COLSPAN = 45;
    public static final int ATTR_HEADERS = 46;
    public static final int ATTR_ROWSPAN = 47;
    public static final int ATTR_SCOPE = 48;
    public static final int ATTR_VALIGN = 49;
    public static final int ATTR_START = 50;
    public static final int ATTR_MEDIA = 51;
    public static final int ATTR_LABEL = 52;
    public static final int ATTR_SUMMARY = 53;
    public static final int ATTR_CONTENT = 54;
    public static final int ATTR_HTTPEQUIV = 55;
    public static final int ATTR_SCHEME = 56;
    public static final int ATTR_COLS = 57;
    public static final int ATTR_ROWS = 58;
    // Unsupported attributes in XHTML-MP that we DO support
    public static final int ATTR_DIR = 59; //Currently only supported in the html tag
    public static final int ATTR_BORDER = 60;
    // Required by HTML4 tags we support:
    public static final int ATTR_COLOR = 61;
    public static final int ATTR_FACE = 62;
    public static final int ATTR_SHAPE = 63;
    public static final int ATTR_COORDS = 64;
    public static final int ATTR_USEMAP = 65;
    // HTML4 attributes
    public static final int ATTR_LANG = 66;
    public static final int ATTR_CELLSPACING = 67;
    public static final int ATTR_CELLPADDING = 68;
    public static final int ATTR_FRAME = 69;
    public static final int ATTR_RULES = 70;
    public static final int ATTR_DISABLED = 71;
    public static final int ATTR_READONLY = 72;
    public static final int ATTR_ISMAP = 73;
    /// ///////////////////////////////

//HTML-MP colors
    public static final int COLOR_AQUA = 0x00ffff;
    public static final int COLOR_BLACK = 0x000000;
    public static final int COLOR_BLUE = 0x0000ff;
    public static final int COLOR_FUCHSIA = 0xff00ff;
    public static final int COLOR_GRAY = 0x808080;


//////////////////////////////////
// Types                        //
    public static final int COLOR_GREEN = 0x008000;
    public static final int COLOR_LIME = 0x00ff00;
    public static final int COLOR_MAROON = 0x800000;
    public static final int COLOR_NAVY = 0x000080;
    public static final int COLOR_OLIVE = 0x808000;
    public static final int COLOR_PURPLE = 0x800080;
    public static final int COLOR_RED = 0xff0000;
    public static final int COLOR_SILVER = 0xc0c0c0;
    public static final int COLOR_TEAL = 0x008080;
    public static final int COLOR_WHITE = 0xffffff;
    public static final int COLOR_YELLOW = 0xffff00;
    public static final int COLOR_ORANGE = 0xffa500; // Orange is added in CSS 2.1
    /**
     * Defines the tag names, these are specified according to the tag constants numbering.
     */
    static final String[] TAG_NAMES = {
            "body", "head", "html", "title"
            , "abbr", "acronym", "address", "blockquote", "br", "cite", "code", "dfn", "div", "em", "h1", "h2", "h3", "h4", "h5", "h6", "kbd", "p", "pre", "q", "samp", "span", "strong", "var"
            , "a"
            , "dl", "dt", "dd", "ol", "ul", "li"
            , "form", "input", "label", "select", "option", "textarea"
            , "caption", "table", "td", "th", "tr"
            , "img"
            , "object", "param"
            , "meta"
            , "link"
            , "base"
            , "hr", "optgroup", "style", "b", "i", "big", "small", "fieldset"
            //html4 tags
            , "u", "font", "del", "ins", "tt", "basefont", "menu", "s", "strike", "center", "dir", "map", "area", "legend", "sub", "sup", "noscript", "noframes"
            , "thead", "tbody", "tfoot", "button"
            , "text"
    };
    /**
     * Defines the allowed attribute names, these are specified according to the ATTR_* constants numbering.
     */
    static final String[] ATTRIBUTE_NAMES = {
            "class", "id", "style", "title", "xmlns", "xml:lang", "align", "bgcolor", "link", "text", "version", "cite",
            "accesskey", "charset", "href", "hreflang", "rel", "rev", "tabindex", "type", "action", "enctype", "method",
            "width", "height", "alt", "hspace", "vspace", "longdesc", "localsrc", "src", "size", "checked", "emptyok",
            "format", "istyle", "maxlength", "name", "value", "for", "xml:space", "multiple", "selected", "abbr", "axis",
            "colspan", "headers", "rowspan", "scope", "valign", "start", "media", "label", "summary", "content", "http-equiv", "scheme",
            "cols", "rows", "dir", "border",
            "color", "face", "shape", "coords", "usemap",
            "lang", "cellspacing", "cellpadding", "frame", "rules",
            "disabled", "readonly", "ismap"
    };
    /// ///////////////////////////////

// These are the possible types for an attribute. Types define what values are acceptable to the attribute.
    static final int TYPE_NUMBER = 0;
    static final int TYPE_PIXELS_OR_PERCENTAGE = 1;
    static final int TYPE_COLOR = 2;
    static final int TYPE_ALIGN = 3; //note: there are different types of align - TD allows only left,center,right DIV allows also justify, CAPTION allows top,bottom,left,right - however we don't get to that resolution
    static final int TYPE_CHAR = 4;
    static final int TYPE_URL = 5;
    static final int TYPE_CDATA = 6;
    static final int TYPE_NMTOKENS = 7;
    static final int TYPE_ID = 8;
    static final int TYPE_XMLNS = 9;
    static final int TYPE_LANG_CODE = 10; // ((?i)[A-Z]{1,8}(-[A-Z]{1,8})*)?
    static final int TYPE_VERSION = 11;
    static final int TYPE_HTTP_METHOD = 12; // get / post
    static final int TYPE_BOOLEAN = 13;
    static final int TYPE_CHECKED = 14;
    static final int TYPE_IDREF = 15;
    static final int TYPE_PRESERVE = 16;
    static final int TYPE_MULTIPLE = 17;
    static final int TYPE_SELECTED = 18;
    static final int TYPE_IDREFS = 19;
    static final int TYPE_SCOPE = 20; // row / col
    static final int TYPE_VALIGN = 21; // top/middle/bottom


//////////////////////////////////
// Colors                       //
    static final int TYPE_NMTOKEN = 22; // top/middle/bottom
    static final int TYPE_DIRECTION = 23; // ltr/rtl
    static final int TYPE_CSS_LENGTH = 24; // values with CSS suffixes (px/em/ex etc.)
    static final int TYPE_CSS_LENGTH_OR_PERCENTAGE = 25; // values with CSS suffixes (px/em/ex etc.) or percentages
    static final int TYPE_CSS_URL = 26;
    static final int TYPE_CSS_LENGTH_OR_PERCENTAGE_OR_MULTIPLIER = 27; // values with CSS suffixes (px/em/ex etc.) or percentages or a number without unit that represents multiply by (i.e. 1 is 100% of the font size, 2 is 200% and so on)
    /**
     * The allowed values for the 'frame' attribute in the 'table' tag
     * (This was not placed in the ALLOWED_STRINGS in order not to add more data types, since it is very specific for this attribute)
     */
    static final String[] ALLOWED_TABLE_FRAME_STRINGS =
            {"void", "above", "below", "hsides", "vsides", "lhs", "rhs", "box", "border"};
    /**
     * The values each string of ALLOWED_TABLE_FRAME_STRINGS represents
     */
    static final int[][] ALLOWED_TABLE_FRAME_VALS =
            {
                    {}, // void
                    {Component.TOP}, // above
                    {Component.BOTTOM}, //below
                    {Component.TOP, Component.BOTTOM}, //hsides
                    {Component.LEFT, Component.RIGHT}, //vsides
                    {Component.LEFT}, //lhs
                    {Component.RIGHT}, // rhs
                    {Component.LEFT, Component.RIGHT, Component.TOP, Component.BOTTOM}, //box
                    {Component.LEFT, Component.RIGHT, Component.TOP, Component.BOTTOM}, //border
            };
    /**
     * The allowed values for the 'rules' attribute in the 'table' tag
     * (This was not placed in the ALLOWED_STRINGS in order not to add more data types, since it is very specific for this attribute)
     */
    static final String[] ALLOWED_TABLE_RULES_STRINGS =
            {"none", "rows", "cols", "all", "groups"};
    /**
     * Defines the allowed color string that are acceptable as a value to color attributes in HTML-MP1
     */
    static final String[] COLOR_STRINGS = {
            "aqua", "black", "blue", "fuchsia", "gray", "green", "lime", "maroon",
            "navy", "olive", "purple", "red", "silver", "teal", "white", "yellow",
            "orange", "grey"
    };
    /**
     * Assigns a color constant to each of the colors defined in COLOR_STRINGS
     */
    static final int[] COLOR_VALS = {
            COLOR_AQUA, COLOR_BLACK, COLOR_BLUE, COLOR_FUCHSIA, COLOR_GRAY, COLOR_GREEN, COLOR_LIME, COLOR_MAROON,
            COLOR_NAVY, COLOR_OLIVE, COLOR_PURPLE, COLOR_RED, COLOR_SILVER, COLOR_TEAL, COLOR_WHITE, COLOR_YELLOW,
            COLOR_ORANGE, COLOR_GRAY
    };
    /**
     * Defines additional allowed color string that are acceptable as a value to color attributes in HTML4
     */
    static final String[] MORE_COLOR_STRINGS = {
            "AliceBlue", "AntiqueWhite", "Aqua", "Aquamarine", "Azure", "Beige", "Bisque", "Black", "BlanchedAlmond", "Blue", "BlueViolet", "Brown", "BurlyWood",
            "CadetBlue", "Chartreuse", "Chocolate", "Coral", "CornflowerBlue", "Cornsilk", "Crimson", "Cyan", "DarkBlue", "DarkCyan", "DarkGoldenRod", "DarkGray",
            "DarkGrey", "DarkGreen", "DarkKhaki", "DarkMagenta", "DarkOliveGreen", "Darkorange", "DarkOrchid", "DarkRed", "DarkSalmon", "DarkSeaGreen",
            "DarkSlateBlue", "DarkSlateGray", "DarkSlateGrey", "DarkTurquoise", "DarkViolet", "DeepPink", "DeepSkyBlue", "DimGray", "DimGrey", "DodgerBlue",
            "FireBrick", "FloralWhite", "ForestGreen", "Fuchsia", "Gainsboro", "GhostWhite", "Gold", "GoldenRod", "Gray", "Grey", "Green", "GreenYellow", "HoneyDew",
            "HotPink", "IndianRed ", "Indigo ", "Ivory", "Khaki", "Lavender", "LavenderBlush", "LawnGreen", "LemonChiffon", "LightBlue", "LightCoral", "LightCyan",
            "LightGoldenRodYellow", "LightGray", "LightGrey", "LightGreen", "LightPink", "LightSalmon", "LightSeaGreen", "LightSkyBlue", "LightSlateGray",
            "LightSlateGrey", "LightSteelBlue", "LightYellow", "Lime", "LimeGreen", "Linen", "Magenta", "Maroon", "MediumAquaMarine", "MediumBlue", "MediumOrchid",
            "MediumPurple", "MediumSeaGreen", "MediumSlateBlue", "MediumSpringGreen", "MediumTurquoise", "MediumVioletRed", "MidnightBlue", "MintCream",
            "MistyRose", "Moccasin", "NavajoWhite", "Navy", "OldLace", "Olive", "OliveDrab", "Orange", "OrangeRed", "Orchid", "PaleGoldenRod", "PaleGreen",
            "PaleTurquoise", "PaleVioletRed", "PapayaWhip", "PeachPuff", "Peru", "Pink", "Plum", "PowderBlue", "Purple", "Red", "RosyBrown", "RoyalBlue",
            "SaddleBrown", "Salmon", "SandyBrown", "SeaGreen", "SeaShell", "Sienna", "Silver", "SkyBlue", "SlateBlue", "SlateGray", "SlateGrey", "Snow", "SpringGreen",
            "SteelBlue", "Tan", "Teal", "Thistle", "Tomato", "Turquoise", "Violet", "Wheat", "White", "WhiteSmoke", "Yellow", "YellowGreen"
    };
    /**
     * Assigns a color constant to each of the colors defined in MORE_COLOR_STRINGS
     */
    static final int[] MORE_COLOR_VALS = {
            0xF0F8FF, 0xFAEBD7, 0x00FFFF, 0x7FFFD4, 0xF0FFFF, 0xF5F5DC, 0xFFE4C4, 0x000000, 0xFFEBCD, 0x0000FF, 0x8A2BE2, 0xA52A2A, 0xDEB887, 0x5F9EA0, 0x7FFF00, 0xD2691E,
            0xFF7F50, 0x6495ED, 0xFFF8DC, 0xDC143C, 0x00FFFF, 0x00008B, 0x008B8B, 0xB8860B, 0xA9A9A9, 0xA9A9A9, 0x006400, 0xBDB76B, 0x8B008B, 0x556B2F, 0xFF8C00, 0x9932CC,
            0x8B0000, 0xE9967A, 0x8FBC8F, 0x483D8B, 0x2F4F4F, 0x2F4F4F, 0x00CED1, 0x9400D3, 0xFF1493, 0x00BFFF, 0x696969, 0x696969, 0x1E90FF, 0xB22222, 0xFFFAF0, 0x228B22,
            0xFF00FF, 0xDCDCDC, 0xF8F8FF, 0xFFD700, 0xDAA520, 0x808080, 0x808080, 0x008000, 0xADFF2F, 0xF0FFF0, 0xFF69B4, 0xCD5C5C, 0x4B0082, 0xFFFFF0, 0xF0E68C, 0xE6E6FA,
            0xFFF0F5, 0x7CFC00, 0xFFFACD, 0xADD8E6, 0xF08080, 0xE0FFFF, 0xFAFAD2, 0xD3D3D3, 0xD3D3D3, 0x90EE90, 0xFFB6C1, 0xFFA07A, 0x20B2AA, 0x87CEFA, 0x778899, 0x778899,
            0xB0C4DE, 0xFFFFE0, 0x00FF00, 0x32CD32, 0xFAF0E6, 0xFF00FF, 0x800000, 0x66CDAA, 0x0000CD, 0xBA55D3, 0x9370D8, 0x3CB371, 0x7B68EE, 0x00FA9A, 0x48D1CC, 0xC71585,
            0x191970, 0xF5FFFA, 0xFFE4E1, 0xFFE4B5, 0xFFDEAD, 0x000080, 0xFDF5E6, 0x808000, 0x6B8E23, 0xFFA500, 0xFF4500, 0xDA70D6, 0xEEE8AA, 0x98FB98, 0xAFEEEE, 0xD87093,
            0xFFEFD5, 0xFFDAB9, 0xCD853F, 0xFFC0CB, 0xDDA0DD, 0xB0E0E6, 0x800080, 0xFF0000, 0xBC8F8F, 0x4169E1, 0x8B4513, 0xFA8072, 0xF4A460, 0x2E8B57, 0xFFF5EE, 0xA0522D,
            0xC0C0C0, 0x87CEEB, 0x6A5ACD, 0x708090, 0x708090, 0xFFFAFA, 0x00FF7F, 0x4682B4, 0xD2B48C, 0x008080, 0xD8BFD8, 0xFF6347, 0x40E0D0, 0xEE82EE, 0xF5DEB3, 0xFFFFFF,
            0xF5F5F5, 0xFFFF00, 0x9ACD32
    };
    /**
     * This array defines the 6 common attributes that each tag has
     */
    private static final int[] COMMON_ATTRIBUTES = {ATTR_CLASS, ATTR_ID, ATTR_STYLE, ATTR_TITLE, ATTR_XMLNS, ATTR_XMLLANG, ATTR_LANG};
    /**
     * This array defines the allowed attributes for each tag, according to the XHTML-MP 1.0 spec
     */
    private static final int[][] TAG_ATTRIBUTES = {
            //Structure Module
            {
                    ATTR_BGCOLOR, // #rrggbb | colors  // Deprecated but supported
                    ATTR_LINK, // #rrggbb | colors     // Deprecated but supported
                    ATTR_TEXT //#rrggbb | colors       // Deprecated but supported
            }, // BODY = 0;
            {}, // HEAD = 1;
            {
                    //ATTR_VERSION, // = //WAPFORUM//DTD XHTML Mobile 1.0//EN // We don't use the version attribute
                    ATTR_DIR

            }, // HTML = 2;
            {}, // TITLE = 3;

            //Text Module -
            {}, // ABBR = 4;
            {}, // ACRONYM = 5;
            {}, // ADDRESS = 6;
            {
                    //ATTR_CITE // URL // Not supported by any of the major browsers
            }, // BLOCKQUOTE = 7;
            {}, // BR = 8;
            {}, // CITE = 9;
            {}, // CODE = 10;
            {}, // DFN = 11;
            {
                    ATTR_ALIGN // top/bottom/left/right
            }, // DIV = 12;
            {}, // EM = 13;
            {
                    ATTR_ALIGN // top/bottom/left/right
            }, // H1 = 14;
            {
                    ATTR_ALIGN // top/bottom/left/right
            }, // H2 = 15;
            {
                    ATTR_ALIGN // top/bottom/left/right
            }, // H3 = 16;
            {
                    ATTR_ALIGN // top/bottom/left/right
            }, // H4 = 17;
            {
                    ATTR_ALIGN // top/bottom/left/right
            }, // H5 = 18;
            {
                    ATTR_ALIGN // top/bottom/left/right
            }, // H6 = 19;
            {}, // KBD = 20;
            {
                    ATTR_ALIGN // left | center | right | justify
            }, // P = 21;
            {
                    //ATTR_XMLSPACE, // preserve // We don't use this attribute
            }, // PRE = 22;
            {
                    //ATTR_CITE // URL // The cite attribute is not supported by any of the major browsers.
            }, // Q = 23;
            {}, // SAMP = 24;
            {}, // SPAN = 25;
            {}, // STRONG = 26;
            {}, // VAR = 27;

            //Hypertext Module -
            {
                    ATTR_ACCESSKEY, // character
                    //ATTR_CHARSET, // cdata // The charset attribute is not supported in any of the major browsers.
                    ATTR_HREF, // URL
                    //ATTR_HREFLANG, // ((?i)[A-Z]{1,8}(-[A-Z]{1,8})*)? // The hreflang attribute is not supported in any of the major browsers.
                    //ATTR_REL, // nmtokens  // Not used by browsers
                    //ATTR_REV, // nmtokens  // Not used by browsers
                    //ATTR_TABINDEX, // number
                    //ATTR_TYPE, // cdata // Should specify the MIME type of the document, but we don't use it anyway
                    ATTR_NAME // Note: Name on the a tag (anchor) is not supported on XHTML-MP 1.0, but we support it
            }, // A = 28;

            //List Module -
            {}, // DL = 29;
            {}, // DT = 30;
            {}, // DD = 31;
            {
                    ATTR_START, // number  // Deprecated but supported
                    ATTR_TYPE // cdata     // Deprecated but supported
            }, // OL = 32;
            {}, // UL = 33;
            {
                    ATTR_TYPE, // cdata    // Deprecated but supported
                    ATTR_VALUE // number   // Deprecated but supported
            }, // LI = 34;
            //Basic Forms Module -
            {
                    ATTR_ACTION, // URL
                    ATTR_ENCTYPE, // cdata
                    ATTR_METHOD, // get | post

            }, // FORM = 35;
            {
                    ATTR_ACCESSKEY, // character
                    ATTR_CHECKED, // checked
                    ATTR_EMPTYOK, // true | false    // This attribute was said to be supported on XHTML-MP1 on various sources, but verified as not
                    ATTR_FORMAT, // cdata            // Deprecated but still supported
                    //ATTR_ISTYLE, // cdata            // This attribute was said to be supported on XHTML-MP1 on various sources, but verified as not
                    //ATTR_LOCALSRC, // cdata          // This attribute was said to be supported on XHTML-MP1 on various sources, but verified as not
                    ATTR_MAXLENGTH, // number
                    ATTR_NAME, // cdata
                    ATTR_SIZE, // cdata
                    ATTR_SRC, // URL
                    ATTR_TABINDEX, // number
                    ATTR_TYPE, // text | password | checkbox | radio | submit | reset | hidden
                    ATTR_VALUE, // cdata
                    ATTR_DISABLED,
                    ATTR_READONLY
            }, // INPUT = 36;
            {
                    ATTR_ACCESSKEY,
                    ATTR_FOR
            }, // LABEL = 37;
            {
                    ATTR_MULTIPLE, // multiple
                    ATTR_NAME, // cdata
                    ATTR_SIZE, // number
                    ATTR_TABINDEX, // number
                    ATTR_DISABLED,
            }, // SELECT = 38;
            {
                    ATTR_SELECTED, // selected
                    ATTR_VALUE, // cdata
                    ATTR_DISABLED,
            }, // OPTION = 39;
            {
                    ATTR_ACCESSKEY, // character
                    ATTR_COLS, // number
                    ATTR_NAME, // cdata
                    ATTR_ROWS, // number
                    ATTR_TABINDEX, // number
                    ATTR_DISABLED,
                    ATTR_READONLY
            }, // TEXTAREA = 40;

            //Basic Tables Module -
            {
                    ATTR_ALIGN // top/bottom/left/right
            }, // CAPTION = 41;
            {
                    //ATTR_SUMMARY, // cdata  // The summary attribute makes no visual difference in ordinary web browsers.
                    ATTR_BORDER,
                    ATTR_CELLSPACING,
                    ATTR_CELLPADDING,
                    ATTR_FRAME,
                    ATTR_RULES,
            }, // TABLE = 42;
            {
                    //ATTR_ABBR, // cdata  // The abbr attribute makes no visual difference in ordinary web browsers.
                    ATTR_ALIGN, // left | center | right
                    //ATTR_AXIS, // cdata  // The axis attribute is not supported by any of the major browsers
                    ATTR_COLSPAN, // number
                    //ATTR_HEADERS, // IDREFS // The headers attribute makes no visual difference in ordinary web browsers.
                    ATTR_ROWSPAN, // number
                    //ATTR_SCOPE, // row | col // // The scope attribute makes no visual difference in ordinary web browsers.
                    ATTR_VALIGN, // top | middle | bottom
                    ATTR_WIDTH,  // number or % - deprecated but still supported
                    ATTR_HEIGHT, // number or % - deprecated but still supported
            }, // TD = 43;
            {
                    //ATTR_ABBR, // cdata  // The abbr attribute makes no visual difference in ordinary web browsers.
                    ATTR_ALIGN, // left | center | right
                    //ATTR_AXIS, // cdata  // The axis attribute is not supported by any of the major browsers
                    ATTR_COLSPAN, // number
                    //ATTR_HEADERS, // IDREFS // The headers attribute makes no visual difference in ordinary web browsers.
                    ATTR_ROWSPAN, // number
                    //ATTR_SCOPE, // row | col // // The scope attribute makes no visual difference in ordinary web browsers.
                    ATTR_VALIGN, // top | middle | bottom
                    ATTR_WIDTH,  // number or % - deprecated but still supported
                    ATTR_HEIGHT, // number or % - deprecated but still supported

            }, // TH = 44;
            {
                    ATTR_ALIGN, // = left | center | right
                    ATTR_VALIGN // top | middle | bottom
            }, // TR = 45;

            //Image Module -
            {
                    ATTR_ALIGN, // top | middle | bottom | left | right
                    ATTR_ALT, // cdata
                    ATTR_HEIGHT, // number[%]
                    ATTR_HSPACE, // number
                    //ATTR_LOCALSRC, // cdata          // This attribute was said to be supported on XHTML-MP1 on various sources, but verified as not
                    //ATTR_LONGDESC, // URL //The longdesc attribute is not supported by any of the major browsers.
                    ATTR_SRC, // URL
                    ATTR_VSPACE, // number
                    ATTR_WIDTH, // number[%]
                    ATTR_USEMAP, // for HTML4
                    ATTR_BORDER, // for HTML4
                    ATTR_ISMAP // for HTML4
            }, // IMG = 46;

            //Object Module -
            {
                    /*    ATTR_ARCHIVE, // &URLs
                        ATTR_CLASSID, // URL
                        ATTR_CODEBASE, // URL
                        ATTR_CODETYPE, // cdata
                        ATTR_DATA, // URL
                        ATTR_DECLARE, // declare
                        ATTR_HEIGHT, // number[%]
                        ATTR_NAME, // cdata
                        ATTR_STANDBY, // cdata
                        ATTR_TABINDEX, // number
                        ATTR_TYPE, // cdata
                        ATTR_WIDTH, // number[%] */
            }, // OBJECT = 47;
            {
                    /* ATTR_NAME, //  cdata
                     ATTR_TYPE, //  cdata
                     ATTR_VALUE, //  cdata
                     ATTR_VALUETYPE // data | ref | object */

            }, // PARAM = 48;

            //Metainformation Module
            {
                    ATTR_CONTENT, // cdata
                    ATTR_HTTPEQUIV, // nmtoken
                    ATTR_NAME, // nmtoken  // We do not make any use of this attribute
                    //ATTR_SCHEME, // cdata  // We do not make any use of this attribute
            }, // META = 49;

            //Link Module -
            {
                    //ATTR_CHARSET, // cdata  //The charset attribute is not supported by any of the major browsers.
                    ATTR_HREF, // URL
                    //ATTR_HREFLANG, // ((?i)[A-Z]{1,8}(-[A-Z]{1,8})*)? // The hreflang attribute is not supported in any of the major browsers.
                    ATTR_MEDIA, // cdata
                    ATTR_REL, // nmtokens
                    //ATTR_REV, // nmtokens // The rev attribute is not supported in any of the major browsers.
                    ATTR_TYPE, // cdata
            }, // LINK = 50;

            //Base Module -
            {
                    ATTR_HREF // URL
            }, // BASE = 51;

            //XHTML-MP
            {
                    ATTR_ALIGN, // left | center | right
                    ATTR_SIZE, // number     // Deprecated but still supported
                    ATTR_WIDTH, // number[%] // Deprecated but still supported

            }, // HR = 52;
            {
                    ATTR_LABEL
            }, // OPTGROUP = 53
            {
                    ATTR_MEDIA, // cdata
                    ATTR_TYPE, // cdata
                    //ATTR_XMLSPACE, // preserve  // We don't use this attribute

            }, // STYLE = 54
            {}, //B = 55;
            {}, //I = 56;
            {}, //BIG = 57;
            {}, //SMALL = 58;
            {}, //FIELDSET = 59;
            {}, //U = 60; // Underline
            {
                    ATTR_COLOR,
                    ATTR_SIZE,
                    ATTR_FACE,
            }, //FONT = 61;
            {}, //DEL = 62;
            {}, //INS = 63;
            {}, //TT = 64;
            {
                    ATTR_COLOR,
                    ATTR_SIZE,
                    ATTR_FACE,
            }, //BASEFONT = 65;
            {}, //MENU = 66; // Same as UL
            {}, //S = 67; // Strike through
            {}, //STRIKE = 68; // Strike through
            {}, //CENTER = 69; // shorthand for DIV align=center
            {}, //DIR = 70;// Same as UL
            {
                    ATTR_NAME
            }, //MAP = 71;
            {
                    ATTR_SHAPE,
                    ATTR_COORDS,
                    ATTR_HREF,
                    ATTR_ALT
            }, //AREA = 72;
            {}, //LEGEND = 73;
            {}, //SUB = 74;
            {}, //SUP = 75;
            {}, //NOSCRIPT = 76;
            {}, //NOFRAMES = 77;
            {
                    ATTR_ALIGN,
                    ATTR_VALIGN
            }, //TAG_THEAD = 78;
            {
                    ATTR_ALIGN,
                    ATTR_VALIGN
            }, //TAG_TBODY = 79;
            {
                    ATTR_ALIGN,
                    ATTR_VALIGN
            }, //TAG_TFOOT = 80;
            {
                    ATTR_ACCESSKEY, // character
                    ATTR_CHECKED, // checked
                    ATTR_EMPTYOK, // true | false    // This attribute was said to be supported on XHTML-MP1 on various sources, but verified as not
                    ATTR_FORMAT, // cdata            // Deprecated but still supported
                    //ATTR_ISTYLE, // cdata            // This attribute was said to be supported on XHTML-MP1 on various sources, but verified as not
                    //ATTR_LOCALSRC, // cdata          // This attribute was said to be supported on XHTML-MP1 on various sources, but verified as not
                    ATTR_MAXLENGTH, // number
                    ATTR_NAME, // cdata
                    ATTR_SIZE, // cdata
                    ATTR_SRC, // URL
                    ATTR_TABINDEX, // number
                    ATTR_TYPE, // text | password | checkbox | radio | submit | reset | hidden
                    ATTR_VALUE, // cdata
                    ATTR_DISABLED,
                    ATTR_READONLY
            },//BUTTON = 81;
            {}, //TEXT = 82;

    };
    /**
     * This array assigns a type to each of the attributes.
     */
    private static final int[] ATTRIBUTE_TYPES = {
            TYPE_NMTOKENS, //"class",
            TYPE_ID, //"id",
            TYPE_CDATA, //"style",
            TYPE_CDATA, //"title",
            TYPE_XMLNS, //"xmlns",
            TYPE_LANG_CODE, //"xml:lang",
            TYPE_ALIGN, //"align",
            TYPE_COLOR, //"bgcolor",
            TYPE_COLOR, //"link",
            TYPE_COLOR, //"text",
            TYPE_VERSION, //"version",
            TYPE_URL, //"cite",
            TYPE_CHAR, //"accesskey",
            TYPE_CDATA, //"charset",
            TYPE_URL, //"href",
            TYPE_LANG_CODE, //"hreflang",
            TYPE_NMTOKENS, //"rel",
            TYPE_NMTOKENS, //"rev",
            TYPE_NUMBER, //"tabindex",
            TYPE_CDATA, //"type",
            TYPE_URL, //"action",
            TYPE_CDATA, //"enctype",
            TYPE_HTTP_METHOD, //"method",
            TYPE_PIXELS_OR_PERCENTAGE, //"width",
            TYPE_PIXELS_OR_PERCENTAGE, //"height",
            TYPE_CDATA, //"alt",
            TYPE_NUMBER, //"hspace",
            TYPE_NUMBER, //"vspace",
            TYPE_URL, //"longdesc",
            TYPE_CDATA, //"localsrc",
            TYPE_URL, //"src",
            TYPE_CDATA, //"size",
            TYPE_CHECKED, //"checked",
            TYPE_BOOLEAN, //"emptyok",
            TYPE_CDATA, //"format",
            TYPE_CDATA, //"istyle",
            TYPE_NUMBER, //"maxlength",
            TYPE_CDATA, //"name",
            TYPE_CDATA, //"value",
            TYPE_IDREF, //"for",
            TYPE_PRESERVE, //"xml:space",
            TYPE_MULTIPLE, //"multiple",
            TYPE_SELECTED, //"selected",
            TYPE_CDATA, //"abbr",
            TYPE_CDATA, //"axis",
            TYPE_NUMBER, //"colspan",
            TYPE_IDREFS, //"headers",
            TYPE_NUMBER, //"rowspan",
            TYPE_SCOPE, //"scope",
            TYPE_VALIGN, //"valign",
            TYPE_NUMBER, //"start",
            TYPE_CDATA, //"media",
            TYPE_CDATA, //"label",
            TYPE_CDATA, //"summary",
            TYPE_CDATA, //"content",
            TYPE_NMTOKEN, //"http-equiv",
            TYPE_CDATA, //"scheme",
            TYPE_NUMBER, //"cols",
            TYPE_NUMBER, //"rows"
            TYPE_DIRECTION, //"dir"
            TYPE_NUMBER, // "border"
            TYPE_COLOR, // "color"
            TYPE_NMTOKEN, // "face"
            TYPE_NMTOKEN, // "shape"
            TYPE_NMTOKEN, // "coords"
            TYPE_NMTOKEN, // "usemap"
            TYPE_NMTOKEN, // "lang"
            TYPE_NUMBER, // "cellspacing"
            TYPE_NUMBER, // "cellpadding"
            TYPE_NMTOKEN, // "frame"
            TYPE_NMTOKEN, // "rules"
            TYPE_NMTOKEN, //ATTR_DISABLED = 71;
            TYPE_NMTOKEN, //ATTR_READONLY = 72;
            TYPE_NMTOKEN, //ATTR_ISMAP = 73;
    };
    // Additional constants used to define allowed characters for specific types
    private static final int DIGITS = 1;
    private static final int HEX = 2;
    private static final int ABC = 4;
    private static final int LAST_TAG_INDEX = HTMLComponent.PROCESS_HTML_MP1_ONLY ? TAG_FIELDSET : TAG_BUTTON; // In any case we exclude TAG_TEXT, which is given only on text element creation
    /**
     * Some types accept only a specific set of strings. For these this array defines the allowed strings.
     * If the value is null it means that the type has another rule set (for example numbers only).
     * This is checked against in the DOM building process.
     */
    private static final String[][] ALLOWED_STRINGS = {
            null, // TYPE_NUMBER = 0;
            null, // TYPE_PIXELS_OR_PERCENTAGE = 1;
            null, // TYPE_COLOR = 2;
            {"left", "right", "top", "bottom", "center", "middle", "justify"}, // TYPE_ALIGN = 3;
            null, // TYPE_CHAR = 4;
            null, // TYPE_URL = 5;
            null, // TYPE_CDATA = 6;
            null, // TYPE_NMTOKENS = 7;
            null, // TYPE_ID = 8;
            null, // TYPE_XMLNS = 9;
            null, // TYPE_LANG_CODE = 10; // ((?i)[A-Z]{1,8}(-[A-Z]{1,8})*)?
            null, // TYPE_VERSION = 11;
            {"get", "post"}, // TYPE_HTTP_METHOD = 12; // get / post
            {"true", "false"}, // TYPE_BOOLEAN = 13;
            {"checked"}, // TYPE_CHECKED = 14;
            null, // TYPE_IDREF = 15;
            {"default", "preserve"}, // TYPE_PRESERVE = 16;
            {"multiple"}, // TYPE_MULTIPLE = 17;
            {"selected"}, // TYPE_SELECTED = 18;
            null, // TYPE_IDREFS = 19;
            {"row", "col"}, // TYPE_SCOPE = 20; // row / col
            {"top", "bottom", "middle"}, // TYPE_VALIGN = 21; // top/middle/bottom
            null, // TYPE_NMTOKEN = 22; // top/middle/bottom
            {"ltr", "rtl"}, // TYPE_DIRECTION
    };
    /**
     * The tag ID. Upon construction of tag with a name, a lookup is performed to assign it an ID accordign to the TAG_NAMES array.
     * THe ID is used to find what are the allowed attributes, and also prevents the need for further string parsing later on.
     */
    private int id = TAG_UNSUPPORTED;


// Member variables:
    /**
     * A vector holding all associate components
     */
    private Vector comps;
    /**
     * If true than the UI components where calculated automatically
     */
    private boolean calculatedUi = false;

    /**
     * Constructs and HTMLElement without specifying a name.
     * This can be used by subclasses that do not require name assigments.
     */
    protected HTMLElement() {
    }


    /**
     * Constructor for HTMLElement. This mostly sets up the element's ID.
     *
     * @param tagName The HTMLElement's name
     */
    public HTMLElement(String tagName) {
        init(tagName);
    }

    /**
     * Constructor for HTMLElement. This mostly sets up the element's ID.
     *
     * @param tagName       The HTMLElement's name, or the text for text elements
     * @param isTextElement true for a text element, false otherwise
     */
    public HTMLElement(String tagName, boolean isTextElement) {
        setTextElement(isTextElement);
        if (isTextElement) {
            setTagName(tagName);
            id = TAG_TEXT;
        } else {
            init(tagName);
        }
    }

    /**
     * Converts a color string into an int value.
     * This method supports color denoted in hex (with a leading #), named colors (from the standard HTML colors) and rgb(x,y,z)
     *
     * @param colorStr     The string representing the color
     * @param defaultColor Default color if color parsing can't be done
     * @return The int value of the parsed color
     */
    static int getColor(String colorStr, int defaultColor) {
        if ((colorStr == null) || (colorStr.equals(""))) {
            return defaultColor;
        }
        if (colorStr.charAt(0) != '#') {

            if (colorStr.startsWith("rgb(")) {
                colorStr = colorStr.substring(4);
                char[] tokens = {',', ',', ')'};
                int weight = 256 * 256;
                int color = 0;
                for (int i = 0; i < 3; i++) {
                    int index = colorStr.indexOf(tokens[i]);
                    if (index == -1) {
                        return defaultColor; // Unparsed color
                    }
                    String channelStr = colorStr.substring(0, index).trim();

                    int channel = HTMLComponent.calcSize(255, channelStr, 0, true);
                    channel = Math.min(channel, 255); // Set to 255 if over 255
                    channel = Math.max(channel, 0); // Set to 0 if negative

                    color += channel * weight;
                    colorStr = colorStr.substring(index + 1);
                    weight /= 256;
                }
                return color;

            } else {
                for (int i = 0; i < COLOR_STRINGS.length; i++) {
                    if (colorStr.equalsIgnoreCase(COLOR_STRINGS[i])) {
                        return COLOR_VALS[i];
                    }
                }
                if (!HTMLComponent.PROCESS_HTML_MP1_ONLY) {
                    for (int i = 0; i < MORE_COLOR_STRINGS.length; i++) {
                        if (colorStr.equalsIgnoreCase(MORE_COLOR_STRINGS[i])) {
                            return MORE_COLOR_VALS[i];
                        }
                    }
                }

            }
        } else {
            colorStr = colorStr.substring(1);
        }

        if (colorStr.length() == 3) { // shortened format rgb - translated to rrggbb
            String newColStr = "";
            for (int i = 0; i < 3; i++) {
                newColStr += colorStr.charAt(i) + "" + colorStr.charAt(i);
            }
            colorStr = newColStr;
        }

        try {
            int color = Integer.parseInt(colorStr, 16);
            return color;
        } catch (NumberFormatException nfe) {
            return defaultColor;
        }
    }

    private void init(String tagName) {
        int i = 0;
        int tagId = -1;

        while ((tagId == -1) && (i <= LAST_TAG_INDEX)) {   // -1 to exclude TAG_TEXT, which is given only on text element creation
            if (TAG_NAMES[i].equals(tagName)) {
                tagId = i;
            } else {
                i++;
            }
        }
        id = tagId;
        if (id == TAG_UNSUPPORTED) {
            setTagName(tagName);
        }
    }

    /**
     * Sets the given component or Vector of components to be associated with this element.
     * This is used internally to apply CSS styling.
     *
     * @param obj The component (or vector of components) representing this HTMLElement
     */
    void setAssociatedComponents(Object obj) {
        if (obj instanceof Vector) {
            comps = (Vector) obj;
        } else {
            comps = new Vector();
            comps.addElement(obj);
        }
    }

    /**
     * Clears the associated components object
     */
    void clearAssociatedComponents() {
        comps = null;
    }

    /**
     * Adds the given component to be associated with this element.
     * This is used internally to apply CSS styling.
     *
     * @param cmp The component to add
     */
    void addAssociatedComponent(Component cmp) {
        if (comps == null) {
            comps = new Vector();
        }
        comps.addElement(cmp);
    }

    /**
     * Adds the given component to be associated with this element.
     * This is used internally to apply CSS styling.
     *
     * @param index The index to insert the component to
     * @param cmp   The component to add
     */
    void addAssociatedComponentAt(int index, Component cmp) {
        if (comps == null) {
            comps = new Vector();
        }
        comps.insertElementAt(cmp, index);
    }


    public void addChild(Element childElement) {
        //if (((HTMLElement)childElement).getId()!=TAG_UNSUPPORTED) {
        super.addChild(childElement);
        //}

    }

    /**
     * Returns whether this element supports the common core attributes.
     * These are attributes most HTML tags support, with a few exceptions that are checked here.
     * Note that to be exact the common atributes are divided to 2 groups: core attributes (class,id,title,style) and language attributes (xmlns,xml:lang)
     * The tags checked here all don't support the core attributes but in fact may support the language attributes.
     * Since the language attributes are not implemented anyway, this is not critical.
     * For reference, tags that do not support the language attributes in XHTML-MP1 are: param, hr, base, br
     *
     * @return true if core attributes are supported, false otherwise
     */
    private boolean supportsCoreAttributes() {
        return ((id != TAG_STYLE) && (id != TAG_META) && (id != TAG_HEAD) && (id != TAG_HTML) && (id != TAG_TITLE) && (id != TAG_PARAM) && (id != TAG_BASE));
    }

    /**
     * Adds the specified attribute and value to this Element if it is supported for the Element and has a valid value.
     *
     * @param attribute The attribute's name
     * @param value     The attribute's value
     * @return a positive error code or -1 if attribute is supported and valid
     */
    public int setAttribute(String attribute, String value) {
        if (id == TAG_UNSUPPORTED) {
            return -1; //No error code for this case since tag not supported error is already notified before
        }

        int attrId = -1;
        int i = 0;
        if (supportsCoreAttributes()) {
            while ((attrId == -1) && (i < COMMON_ATTRIBUTES.length)) {
                if (ATTRIBUTE_NAMES[COMMON_ATTRIBUTES[i]].equals(attribute)) {
                    attrId = COMMON_ATTRIBUTES[i];
                } else {
                    i++;
                }
            }
        }

        i = 0;
        while ((attrId == -1) && (i < TAG_ATTRIBUTES[id].length)) {
            if (ATTRIBUTE_NAMES[TAG_ATTRIBUTES[id][i]].equals(attribute)) {
                attrId = TAG_ATTRIBUTES[id][i];
            } else {
                i++;
            }
        }

        if (attrId == -1) {
            return HTMLCallback.ERROR_ATTRIBUTE_NOT_SUPPORTED;

        } else {
            if (isValid(ATTRIBUTE_TYPES[attrId], value)) {
                setAttribute(Integer.valueOf(attrId), value);
            } else {
                return HTMLCallback.ERROR_ATTIBUTE_VALUE_INVALID;
            }
        }

        return -1;
    }

    /**
     * Allows setting an attribute with an attribute id
     *
     * @param attrId The attribute Id (One of the ATTR_ constants)
     * @param value  The value to set to the attribute
     */
    public void setAttributeById(int attrId, String value) {
        if ((attrId < 0) || (attrId >= ATTRIBUTE_NAMES.length)) {
            throw new IllegalArgumentException("Attribute Id must be in the range of 0-" + (ATTRIBUTE_NAMES.length - 1));
        }
        if (isValid(ATTRIBUTE_TYPES[attrId], value)) {
            setAttribute(Integer.valueOf(attrId), value);
        } else {
            throw new IllegalArgumentException(value + " is not a valid value for attribute " + ATTRIBUTE_NAMES[attrId]);
        }
    }

    /**
     * Removes the specified attribute
     *
     * @param attrId The attribute Id (One of the ATTR_ constants)
     */
    public void removeAttributeById(int attrId) {
        if ((attrId < 0) || (attrId >= ATTRIBUTE_NAMES.length)) {
            throw new IllegalArgumentException("Attribute Id must be in the range of 0-" + (ATTRIBUTE_NAMES.length - 1));
        }
        removeAttribute(Integer.valueOf(attrId));
    }

    /**
     * Returns a list of supported attributes for this tag. Note that the list does not include the core attributes that are supported on almost all tags
     *
     * @return a list of supported attributes for this tag
     */
    public String getSupportedAttributesList() {
        if ((id < 0) || (id >= TAG_ATTRIBUTES.length)) {
            return "Unknown";
        }
        String list = "";
        for (int a = 0; a < TAG_ATTRIBUTES[id].length; a++) {
            list += ATTRIBUTE_NAMES[TAG_ATTRIBUTES[id][a]] + ",";
        }
        if (supportsCoreAttributes()) {
            for (int a = 0; a < COMMON_ATTRIBUTES.length; a++) {
                list += ATTRIBUTE_NAMES[COMMON_ATTRIBUTES[a]] + ",";
            }
        }

        if (list.endsWith(",")) {
            list = list.substring(0, list.length() - 1);
        }
        if (list.equals("")) {
            list = "None";
        }
        return list;
    }

    /**
     * Verifies that the specified value conforms with the attribute's type restrictions.
     * This basically checks the attribute type and according to that checks the value.
     *
     * @param attrId The attribute ID
     * @param value  The value to be checked
     * @return true if the value is valid for this attribute, false otherwise
     */
    private boolean isValid(int type, String value) {
        if (value == null) { // a null value is invalid for all attributes
            return false;
        }
        if (ALLOWED_STRINGS[type] != null) {
            return verifyStringGroup(value, ALLOWED_STRINGS[type]);
        }

        switch (type) {
            case TYPE_NUMBER:
                return verify(value, DIGITS, null);
            case TYPE_PIXELS_OR_PERCENTAGE:
                if (value.endsWith("%")) { //percentage
                    value = value.substring(0, value.length() - 1);
                } else if (value.endsWith("px")) { //pixels
                    value = value.substring(0, value.length() - 2);
                }
                return verify(value, DIGITS, null);
            case TYPE_CHAR:
                return verify(value, DIGITS | ABC, null, 1, 1);
            case TYPE_COLOR:
                if (value.length() == 0) {
                    return false;
                }
                if (value.charAt(0) != '#') {
                    return verifyStringGroup(value, COLOR_STRINGS);
                } else {
                    return verify(value.substring(1), HEX, null, 3, 6); //Color can also be #rgb which means #rrggbb (as for 4,5 chars these will also be tolerated)
                }

            default:
                return true;
        }
    }

    /**
     * A convenience method for verifying strings with no length restrictions
     *
     * @param value        The string to be checked
     * @param allowedMask  DIGITS or HEX or ABC or a combination of those
     * @param allowedChars Characters that are allowed even if they don't conform to the mask
     * @return true if the string is valid, false otherwise.
     */
    private boolean verify(String value, int allowedMask, char[] allowedChars) {
        return verify(value, allowedMask, allowedChars, -1, -1);
    }

    /**
     * Verifies that the specified string conforms with the specified restrictions.
     *
     * @param value        The string to be checked
     * @param allowedMask  DIGITS or HEX or ABC or a combination of those
     * @param allowedChars Characters that are allowed even if they don't conform to the mask
     * @param minLength    Minimum length
     * @param maxLength    Maximum length
     * @return true if the string is valid, false otherwise.
     */
    private boolean verify(String value, int allowedMask, char[] allowedChars, int minLength, int maxLength) {
        if ((minLength != -1) && (value.length() < minLength)) {
            return false;
        }
        if ((maxLength != -1) && (value.length() > maxLength)) {
            return false;
        }


        int i = 0;
        while (i < value.length()) {
            boolean found = false;
            char ch = value.charAt(i);
            if ((allowedMask & HEX) != 0) {
                if (((ch >= '0') && (ch <= '9')) ||
                        ((ch >= 'A') && (ch <= 'F')) ||
                        ((ch >= 'a') && (ch <= 'f'))) {
                    found = true;
                }
            }

            if ((allowedMask & DIGITS) != 0) {
                if (((ch >= '0') && (ch <= '9'))) {
                    found = true;
                } else if ((i == 0) && ((ch == '-') || (ch == '+'))) { // Sign is allowed as the first character
                    found = true;
                }
            }

            if ((!found) && ((allowedMask & ABC) != 0)) {
                if (((ch >= 'a') && (ch <= 'z')) ||
                        ((ch >= 'A') && (ch <= 'Z'))) {
                    found = true;
                }
            }

            if ((!found) && (allowedChars != null)) {
                int c = 0;
                while ((!found) && (c < allowedChars.length)) {
                    if (ch == allowedChars[c]) {
                        found = true;
                    } else {
                        c++;
                    }
                }
            }

            if (!found) {
                return false;
            }
            i++;

        }
        return true;
    }

    /**
     * Verifies that the specified string equals to one of the allowed strings
     *
     * @param value   The string to be checked
     * @param allowed The list of allowed strings
     * @return true if the string equals to one of the allowed, false otherwise
     */
    boolean verifyStringGroup(String value, String[] allowed) {
        for (int i = 0; i < allowed.length; i++) {
            if (value.equalsIgnoreCase(allowed[i])) {
                return true;
            }
        }
        return false;
    }

    /**
     * Returns this HTMLElement's tag name
     *
     * @return the HTMLElement's tag name
     */
    public String getTagName() {
        String name = super.getTagName();

        if (name != null) { //unsupported tags
            return name;
        }
//        if ((id<0) || (id>=TAG_NAMES.length)) {
//            return "Unsupported";
//        }
        return TAG_NAMES[id];
    }

    /**
     * Returns this HTMLElement's ID
     *
     * @return the HTMLELement's ID
     */
    public int getTagId() {
        return id;
    }

    /**
     * Sets this HTMLElement's ID
     *
     * @param tagId The tag ID to set, one of the TAG_* constants (Not to be confused with the id attribute)
     */
    protected void setTagId(int tagId) {
        this.id = tagId;
    }

    /**
     * Returns an HTMLElement's child by a tag ID (One of the TAG_* constants)
     *
     * @param tagId The child's tag ID, one of the TAG_* constants (Not to be confused with the id attribute)
     * @return the first child with the specified ID, or null if not found
     */
    public HTMLElement getFirstChildByTagId(int tagId) {
        Vector children = getChildren();
        if (children == null) {
            return null;
        }
        int i = 0;
        HTMLElement found = null;
        while ((found == null) && (i < children.size())) {
            HTMLElement child = (HTMLElement) children.elementAt(i);
            if (child.getTagId() == tagId) {
                found = child;
            } else {
                i++;
            }
        }
        return found;

    }

    /**
     * Returns an HTMLElement's attribute by the attribute's ID (One of the ATTR_* constants)
     *
     * @param id The attribute's ID
     * @return the attribute with the specified ID, or null if not found
     */
    public String getAttributeById(int id) {
        Hashtable attributes = getAttributes();
        if (attributes == null) {
            return null;
        }
        return (String) attributes.get(Integer.valueOf(id));
    }

    /**
     * {{@inheritDoc}}
     */
    public String toString() {
        return toString("");
    }


    /**
     * Returns the attribute name of the requested attribute
     *
     * @param attrKey The attribute key, which is typically an Integer object made of its int attrId
     * @return the attribute name of the requested attribute
     */
    public String getAttributeName(Integer attrKey) { // This method is not static since it needs to be overriden in CSSElement
        return ATTRIBUTE_NAMES[attrKey.intValue()];
    }

    public String getAttribute(String name) {
        Hashtable attributes = getAttributes();
        if (attributes != null) {
            for (int i = 0; i < ATTRIBUTE_NAMES.length; i++) {
                if (name.equalsIgnoreCase(ATTRIBUTE_NAMES[i])) {
                    return getAttributeById(i);
                }
            }
        }
        return null;
    }

    /**
     * A recursive method for creating a printout of a full tag with its entire hierarchy.
     * This is used by the public method toString().
     *
     * @param spacing Increased by one in each recursion phase to provide with indentation
     * @return the printout of this tag
     */
    public String toString(String spacing) {
        String str = spacing;
        if (!isTextElement()) {
            str += "<" + getTagName();
            Hashtable attributes = getAttributes();
            if (attributes != null) {
                for (Enumeration e = attributes.keys(); e.hasMoreElements(); ) {
                    Integer attrKey = (Integer) e.nextElement();
                    String attrStr = getAttributeName(attrKey);

                    String val = (String) attributes.get(attrKey);
                    str += " " + attrStr + "='" + val + "' (" + attrKey + ")";
                }
            }
            str += ">\n";

            Vector children = getChildren();
            if (children != null) {
                for (int i = 0; i < children.size(); i++) {
                    str += ((HTMLElement) children.elementAt(i)).toString(spacing + ' ');
                }
            }
            str += spacing + "</" + getTagName() + ">\n";
        } else {
            str += "'" + getText() + "'\n";
        }
        return str;
    }

    /**
     * Returns a vector of Components associated with this HTMLElement
     *
     * @return a vector of Components associated with this HTMLElement
     */
    Vector getUi() {
        if (comps == null) { // If no UI exists this may be a tag with children that do have UI, such as TAG_A
            comps = new Vector();
            Vector children = getChildren();
            if (children != null) {
                for (Enumeration e = children.elements(); e.hasMoreElements(); ) {
                    HTMLElement child = (HTMLElement) e.nextElement();
                    Vector childUI = child.getUi();
                    for (Enumeration e2 = childUI.elements(); e2.hasMoreElements(); ) {
                        comps.addElement(e2.nextElement());
                    }
                }
            }
            calculatedUi = true;
        }
        return comps;
    }

    /**
     * Causes a recalculation of the UI, if the UI of this element was deduced from children components
     */
    void recalcUi() {
        if (calculatedUi) {
            comps = null;
            calculatedUi = false;
        }
    }

    /**
     * {{@inheritDoc}}
     */
    public void removeChildAt(int index) { // Overiding is done to clear the associated components vector of the child
        HTMLElement child = (HTMLElement) getChildAt(index);
        child.clearAssociatedComponents();
        super.removeChildAt(index);
    }

    private void getDescendantsByTagIdInternal(Vector v, int tagId, int depth) {
        int i = 0;
        Vector children = getChildren();
        if (children != null) {
            while (i < children.size()) {
                HTMLElement child = (HTMLElement) children.elementAt(i);
                if (depth > 0) {
                    child.getDescendantsByTagIdInternal(v, tagId, depth - 1);
                }
                if (child.getTagId() == tagId) {
                    v.addElement(child);
                }
                i++;
            }
        }

    }

    /**
     * Returns all descendants with the specified tag id
     *
     * @param tagId The tag ID to look for, one of the TAG_* constants (Not to be confused with the id attribute)
     * @param depth The search depth (1 - children, 2 - grandchildren .... DEPTH_INFINITE - for all descendants)
     * @return A vector containing descendants with the specified tag id
     */
    public Vector getDescendantsByTagId(int tagId, int depth) {
        if (depth < 1) {
            throw new IllegalArgumentException("Depth must be 1 or higher");
        }
        if (isEmpty()) {
            return null;
        }
        Vector v = new Vector();
        getDescendantsByTagIdInternal(v, tagId, depth);
        return v;
    }

    /**
     * Returns all descendants with the specified tag id
     *
     * @param tagId The tag ID to look for, one of the TAG_* constants (Not to be confused with the id attribute)
     * @return A vector containing descendants with the specified tag id
     */
    public Vector getDescendantsByTagId(int tagId) {
        return getDescendantsByTagId(tagId, DEPTH_INFINITE);
    }

    /**
     * Returns true if this element is the first non-text child of its parent
     * This is used internally for the :first-child pseudo class
     *
     * @return true if this element is the first non-text child of its parent
     */
    boolean isFirstChild() {
        if ((HTMLComponent.PROCESS_HTML_MP1_ONLY) || (isTextElement())) { // :first-child is not supported in HTML-MP1
            return false;
        }
        HTMLElement parent = (HTMLElement) getParent();
        if (parent != null) {
            for (Element e : parent) {
                HTMLElement elem = (HTMLElement) e;
                if (elem == this) {
                    return true;
                }
                if (!elem.isTextElement()) {
                    return false;
                }
            }
        }
        return false;
    }

}

